<template>
    <view>
        <base-node-vue :followTheme="props.followTheme" :fieldNames="props.fieldNames" :data="listData"></base-node-vue>
    </view>
</template>
<script lang="ts" setup>
/**
 * 树状结构
 * @description 用来展示层级，文件夹结构体等.
 */
import { ref,computed ,provide,reactive, watch,readonly ,toRaw} from "vue"
import { baseNodeData } from "./interface";
import baseNodeVue from "./base-node.vue";
import { getNodeRouter,treeFlat,queryNodeIsParent ,queryParentNode} from "./util";
/**
 * 事件说明
 * node-click:节点标题点击时触发的事件。
 * check:节点复选框状态变化时触发的事件。
 * expand:父节点展开/关闭时触发的事件。
 */
const emits = defineEmits(['node-click','check','expand','update:selectedId','update:expandedId'])
const props = defineProps({
	followTheme: {
		type: [Boolean,String],
		default: true
	},
    color:{
        type:String,
        default:'primary'
    },
    //父节点打开时的图标。
    expandedIconOpen:{
        type:String,
        default:'tmicon-sort-down'
    },
    //父节点被关闭时显示的图标。
    expandedIconClose:{
        type:String,
        default:'tmicon-caret-right'
    },
    //允许在节点前添加复选框。
    checkable:{
        type:Boolean,
        default:true
    },
    //是否允许多选
    multiple:{
        type:Boolean,
        default:true
    },

    /**
     * 展开的父节点可以使用v-model:expanded-id
     */
    expandedId:{
        type:Array,
        default:()=>[]
    },
    /**
     * 默认展开的父节点
     */
    defaultExpandedId:{
        type:Array,
        default:()=>[]
    },
    //选中的节点。可以使用v-model:selected-id
    selectedId:{
        type:Array,
        default:()=>[]
    },
    //默认选中的节点
    defaultSelectedId:{
        type:Array,
        default:()=>[]
    },
    /**
     * 生成树结构的数据。结构必须要有id字段。当然可以通过field-names来映射，如果你的唯一标识字段不是Id的话。
     */
    data:{
        type:Array,
        default:()=>[],
        required:true
    },
    /**
     * 数据结构字段的映射表。
     */
    fieldNames:{
        type:Object,
        default:()=>{
            return {
                id:'id',
                text:'text'
            }
        }
    },
    //显示连线。
    showLine:{
        type:[Boolean,String],
        default:true
    }
})
let listData = ref(props.data)
//所有的节点被平铺为一维。当data改变时就要取交集数据，差集需要删除。
let listDataFlat = [...treeFlat(props.data,props.fieldNames.id)];
//所有父节点
let listParentFlat = queryParentNode(props.data,props.fieldNames.id);
const selectedIds = ref([...new Set([...props.defaultSelectedId,...props.selectedId])])
const SelectedExpandedId = ref([...new Set([...props.defaultExpandedId,...props.expandedId])])
//保存选中的父节点。
const parentIds = ref<Array<string|number>>([])

//是否单选。
if(!props.multiple&& selectedIds.value.length>=1){
    selectedIds.value = [selectedIds.value[0]]
}

//分离父和子节点。
let __selectedIdsSet = new Set(listParentFlat)
parentIds.value  = selectedIds.value.filter(el=>__selectedIdsSet.has(el))

//过滤错误的id集。意思是如果用户提供的id不在数据props.data结构体中，就需要排除掉。以增加使用者的容错率。
const ___a_selectedIds = new Set(selectedIds.value)
selectedIds.value = listDataFlat.filter(el=>___a_selectedIds.has(el))
selectedIds.value = selectedIds.value.filter(el=>!__selectedIdsSet.has(el))

const ___a_ExpandedId = new Set(SelectedExpandedId.value)
SelectedExpandedId.value = listDataFlat.filter(el=>___a_ExpandedId.has(el))


//向下传递当前默认选中的子节点。
provide("TreePareantSelectedIds",readonly(selectedIds))
//向下传递默认打开的父节点
provide("TreePareantSelectedExpandedId",readonly(SelectedExpandedId))
//父节点
provide("TreeParentIds",readonly(parentIds))

function onSelected(key:Array<string|number>){
    selectedIds.value = [...new Set([...selectedIds.value,...key])];
    emits('update:selectedId',selectedIds.value)
}
function onUnSelected(key:Array<string|number>){
    let a = new Set(key)
    selectedIds.value = [...new Set([...selectedIds.value].filter(el=>!a.has(el)))];
    emits('update:selectedId',selectedIds.value)
}
function onCheck(e:baseNodeData){
    emits('check',e)
}
function onExpand(e:any){
    SelectedExpandedId.value = [...new Set([...SelectedExpandedId.value,e.data[props.fieldNames.id]])];
    emits('expand',{...e.data,expanded:e.expand})
    emits('update:expandedId',SelectedExpandedId.value)
}
function onUnExpand(e:any){
    SelectedExpandedId.value = [...new Set([...SelectedExpandedId.value].filter(el=>el!=e.data[props.fieldNames.id]))];
    emits('expand',{...e.data,expanded:e.expand})
    emits('update:expandedId',SelectedExpandedId.value)
}
function onSelectedParent(key:Array<string|number>){
    parentIds.value = [...new Set([...parentIds.value,...key])];
    
}
function onUnSelectedParent(key:Array<string|number>){
    let a = new Set(key)
    parentIds.value = [...new Set([...parentIds.value].filter(el=>!a.has(el)))];
}
function getAllListData(){
    return toRaw(props.data);
}
watch([()=>props.expandedId],()=>{
    SelectedExpandedId.value = [...props.expandedId];
})
watch([()=>props.data],()=>{
    listData.value = props.data
    listDataFlat = [...treeFlat(props.data)];
    //所有父节点
    listParentFlat = queryParentNode(props.data,props.fieldNames.id);
        //分离父和子节点。
    let __selectedIdsSet = new Set(listParentFlat)
    parentIds.value  = selectedIds.value.filter(el=>__selectedIdsSet.has(el))

    let _a_selectedIds = new Set(selectedIds.value)
    let s = listDataFlat.filter(el=>_a_selectedIds.has(el))
    selectedIds.value = s.filter(el=>!__selectedIdsSet.has(el))
    emits('update:selectedId',selectedIds.value)
    let _a_ExpandedId = new Set(SelectedExpandedId.value)
    SelectedExpandedId.value = listDataFlat.filter(el=>_a_ExpandedId.has(el))
    emits('update:expandedId',SelectedExpandedId.value)

},{deep:true})
provide('TreeNodeCheckable',computed(()=>props.checkable))

/** 下方的方法是对象展示的ref方法 **/
/**
 * 改变所有节点状态。
 * checked:
 * true时选中所有节点。
 * false时取消所有节点
 */
function checkAll(checked = true){
    if(checked==true){
        onSelected(listDataFlat);
        onSelectedParent(listParentFlat)
		
    }else{
        selectedIds.value = [];
        onUnSelectedParent(listParentFlat)
    }
    emits('update:selectedId',[...selectedIds.value,...parentIds.value])
}
/**
 * 指定节点状态
 * @param {string|number} key 节点id
 * @param {boolean} checked 节点状态
 * description 注意，如果指定的是父节点，将会选中他的所有节节点，反之取消它所有的子节点。
 */
function checkNode(key:string|number,checked:boolean){
	//先检查 是否存在节点，不存在返回失败。
	if(!new Set(listDataFlat).has(key)){
		console.error("不存在该节点")
		return false;
	}
	let parentData = queryNodeIsParent(toRaw(props.data),key,props.fieldNames.id);
    //选中的是父节点
	if(parentData){
        //拆分父和子节点。
        
        //先平铺所有节点。
       let flat_nodataNodes =  treeFlat(parentData.children,props.fieldNames.id)
       let all_parent = new Set(listParentFlat)
        //分离出父节点。
        let flat_nodataParentNodes = [...flat_nodataNodes,key].filter(el=>all_parent.has(el))
        //分离也所有子节点
        let flat_nodataChildNodes = [...flat_nodataNodes,key].filter(el=>!all_parent.has(el))

		if(checked==true){
			onSelected(flat_nodataChildNodes);
            onSelectedParent(flat_nodataParentNodes)
		}else{
			onUnSelected(flat_nodataChildNodes);
            onUnSelectedParent(flat_nodataParentNodes)
		}
	}else{
		if(checked==true){
			onSelected([key]);
		}else{
			onUnSelected([key]);
		}
	}
	emits('update:selectedId',[...selectedIds.value,...parentIds.value])
	return true;
}
/**
 * 展开或者关闭所有父节点状态
 * @param {Boolean}  checked 指定节点打开还是状态的状态。
 */
function expandAll(checked = true){
	let flatarray = queryParentNode(toRaw(props.data),props.fieldNames.id);
	if(checked){
		SelectedExpandedId.value = [...new Set([...SelectedExpandedId.value,...flatarray])]
	}else{
		SelectedExpandedId.value = [];
	}
	emits('update:expandedId',SelectedExpandedId.value)
}
/**
 * 指定父节点展开状态
 * @param {string|number} key 节点id
 * @param {boolean} checked 节点状态
 */
function expandNode(key:string|number,checked:boolean){
	//先检查 是否存在节点，不存在返回失败。
	if(!new Set(listDataFlat).has(key)){
		console.error("不存在该节点")
		return false;
	}
	let parentData = queryNodeIsParent(toRaw(props.data,props.fieldNames.id),key);
	if(!parentData){
		console.error("该节点非父节点")
		return false
	}
	if(checked==true){
		SelectedExpandedId.value = [...new Set([...SelectedExpandedId.value,key])]
	}else{
		SelectedExpandedId.value = [...new Set(SelectedExpandedId.value.filter(el=>el!=key))]
	}
	emits('update:expandedId',SelectedExpandedId.value)
	return true;
}
/**
 * 显示某一节点
 * @param key 需要要打开所在路径显示的节点
 * @description 将会打开它所在的所有父节点
 */
function showNode(key:string|number){
    //先检查 是否是父节点。
    let nodepath = getNodePath(key);
    let parentData = queryNodeIsParent(toRaw(props.data),key,props.fieldNames.id);
    if(!parentData){
         nodepath = nodepath.filter(el=>el!=key);
    }
   
    SelectedExpandedId.value = [...new Set([...SelectedExpandedId.value,...nodepath])]
    emits('update:expandedId',SelectedExpandedId.value)
}
/**
 * 获取选中的节点key数组
 * @param {String}  strategy all:返回所有选中的节点,parent:父子节点都选中时只返回父节点,children:只返回子节点
 */
function getCheckedNodes(strategy='all'){
	if(strategy=='all'){
		return [...toRaw(selectedIds.value),...parentIds.value]
	}else if(strategy=='parent'){
		
		return toRaw(parentIds.value)
	}else if(strategy=='children'){
		
		return toRaw(selectedIds.value)
	}else{
		return [];
	}
}
/**
 * 获取当前展开的节点
 */
function getExpandedNodes(){
    return toRaw(SelectedExpandedId.value)
}
/**
 * 反回节点路径
 * @param key 节点id
 * @description 从父节点开始一直到本节点的路径数组。
 */
function getNodePath(key:string|number){
    return getNodeRouter(toRaw(props.data),key,props.fieldNames.id)
}


/**
 * 搜索数据
 * @param keyword 关键词
 */
function searchData(keyword) {
    const loop = (data) => {
        const result = [];
        data.forEach(item => {
        if (item[props.fieldNames.text].toLowerCase().indexOf(keyword.toLowerCase()) > -1) {
            result.push({...item});
        } else if (item.children) {
            const filterData = loop(item.children);
            if (filterData.length) {
            result.push({
                ...item,
                children: filterData
            })
            }
        }
        })
        return result;
    }

    return loop(originTreeData);
}
defineExpose({TreeParaentName:"tmTree",onUnSelected,onSelected,onCheck,onExpand,onUnExpand,onSelectedParent,onUnSelectedParent,getAllListData,
    checkAll,checkNode,expandAll,expandNode,getCheckedNodes,getExpandedNodes,getNodePath,showNode
})
</script>
